#include "device/air780e/AtNetworkSocketAir780E.h"

#define GSM_SOCKET_OPEN_CMD "AT+CIPSTART=%d,\"%s\",\"%s\",%d\r\n" // 打开Socket端口连接
#define GSM_SOCKET_OPEN_RES "CONNECT OK"                          // 连接打开成功回调

#define GSM_SOCKET_SEND_CMD "AT+CIPSEND=%d,%d\r\n" // 发送数据包
#define GSM_SOCKET_SEND_START '>'                  // 开始数据位标记

#define GSM_SOCKET_CLOSE_CMD "AT+CIPCLOSE=%d\r\n" // 关闭Socket连接指令

/**
 * URC回调接口函数
 */
void AtNetworkSocketAir780E::urcWithClose(void *param) {
  this->device->remove_urc_callback(this->urcRecv);

  this->_connected = 0;
  this->_error = 0;
}

/**
 * URC回调接口函数
 */
void AtNetworkSocketAir780E::urcWithRecv(void *param) {
  int length = this->device->streamGetInt(8, '\n');

  for (size_t index = 0; index < length; index++) {
    while (!this->device->_stream->available()) {
      AT_YIELD();
    }

    int t = this->device->_stream->read();

    this->queue->push(t);

    AT_YIELD();
  }
}

/**
 * 写入一段数据
 * @param buf 数据
 * @param size 长度
 */
size_t AtNetworkSocketAir780E::write0(const uint8_t *buf, size_t size) {
  this->device->streamPrintfClear(GSM_SOCKET_SEND_CMD, this->cid, size);

  if (this->device->streamSkipUntil(GSM_SOCKET_SEND_START)) {
    size_t r = this->device->_stream->write(buf, size);
    if (this->device->wait(AT_OK) == AT_RCODE1) {
      this->_error = 0;

      return r;
    }
  }

  // 错误计数超过3次视为连接中断
  if (this->_error++ >= AT_SOCKET_ERR) {
    this->disconnect0(this->cid);
  }

  return CONNECT_ERR_0;
}

/**
 * 连接到指定IP:PORT
 * @param host IP地址
 * @param port 端口
 */
int AtNetworkSocketAir780E::connect0(const char *host, uint16_t port) {
  this->device->streamPrintfClear(GSM_SOCKET_OPEN_CMD, this->cid, this->mode, host, port);
  if (this->device->wait(5000, GSM_SOCKET_OPEN_RES) == AT_RCODE1) {
    this->device->add_urc_callback(this->urcRecv);

    this->_connected = 1;
    this->_error = 0;

    return 1;
  }

  return 0;
}

/**
 * 断开连接
 */
void AtNetworkSocketAir780E::disconnect0(uint8_t cid) {
  this->device->streamPrintfClear(GSM_SOCKET_CLOSE_CMD, cid);

  if (this->device->wait(AT_OK) == AT_RCODE1) {
    this->urcWithClose(NULL);
  }
}